/*****************************************************************************
 * 
 * Copyright [2013] [Mervin.Wong]
 * 
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 * 
 *       http://www.apache.org/licenses/LICENSE-2.0
 *       
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *  
 *****************************************************************************/
package model;

import java.util.HashMap;

import util.D;
import util.MathTool;
import core.NetModel;
import core.Global.NetType;
import core.Global.NumberType;
import feature.Degree;

/*********************************************************************************
 *
 * 生成BA模型的方式：1，在随机网络上生成；2，在已构造的网络上生成
 * BA模型的参数：1，基础网络（生成BA模型的方式）
 * 				 2，BA模型新增网络节点的数量
 * 				 3，一个新增节点连接到网络中m个节点，m值的确定
 *
 **********************************************************************************/
/**
 *  BaNetwork
 *  BA无标度网络模型
 * @author 王进法<Mervin.Wong>
 * @date 2013-1-29 上午9:46:23
 * @version 0.1.0
 */
public class BANetwork extends NetModel{
	private int addNodeNum = 0;//添加节点的数量
	private int m = 0;
	
	private int degreeSum = 0;//网络中节点度的总和
	private int nodeDegreeMax = 0; //网络中节点度的最大值
	private Degree degree = null;
	private HashMap<Number, Number> nodeDegree = null;//网络的节点度
	
	
	public BANetwork(int initNodeNum){
		super(initNodeNum);
		this.degree = new Degree(this);
		this.nodeDegree = degree.nodesDegree(this.topology.keySet());
		this.nodeDegreeMax = degree.nodesDegreeMax(this.nodeDegree);
		if(this.netType.equals(NetType.UNDIRECTED_NET)){
			//无向网络
			this.degreeSum = this.edgeNum*2;
		}else {
			//有向网络
			this.degreeSum = this.edgeNum;
		}
	}
	public BANetwork(String fileName, NetType netType){
		super(fileName, netType);
		this.degree = new Degree(this);
		this.nodeDegree = degree.nodesDegree(this.topology.keySet());
		this.nodeDegreeMax = degree.nodesDegreeMax(this.nodeDegree);
		if(this.netType.equals(NetType.UNDIRECTED_NET)){
			//无向网络
			this.degreeSum = this.edgeNum*2;
		}else {
			//有向网络
			this.degreeSum = this.edgeNum;
		}
	}
	public BANetwork(String fileName, NetType netType, NumberType numberType){
		super(fileName, netType, numberType);
		this.degree = new Degree(this);
		this.nodeDegree = degree.nodesDegree(this.topology.keySet());
		this.nodeDegreeMax = degree.nodesDegreeMax(this.nodeDegree);
		if(this.netType.equals(NetType.UNDIRECTED_NET)){
			//无向网络
			this.degreeSum = this.edgeNum*2;
		}else {
			//有向网络
			this.degreeSum = this.edgeNum;
		}
	}
	
	/**
	 *  setNetParam
	 *  设置模型参数
	 * @param addNodeNum
	 * @param m
	 */
	public void setNetParam(int addNodeNum, int m){
		this.addNodeNum = addNodeNum;
		this.m = m;
	}
	/**
	 *  createModelNetwork
	 *  由BA算法来演化网络结构
	 * @return void
	 */
	@Override
	public void createModelNetwork() {
		// TODO Auto-generated method stub
		Number currentNodeId = null;
		if(this.getNumberType().equals(NumberType.LONG)){
			currentNodeId = this.maxNodeId.longValue()+1;
		}else{
			currentNodeId = this.maxNodeId.intValue()+1;
		}
		
		Number oldNodeId = null;
		for(int i = 0; i < this.addNodeNum; i++){
			/** 1,产生一个新节点 currentNodeId*/
			/** 2, 与已存在的m个节点相连 */
			for(int j = 0; j < m; j++){
				oldNodeId = this.prioritySelectionFun(currentNodeId);
				D.m(currentNodeId+"##"+oldNodeId);
				this.insertEdge(currentNodeId, oldNodeId);
			}
			if(this.getNumberType().equals(NumberType.LONG)){
				currentNodeId = currentNodeId.longValue()+1;
			}else{
				currentNodeId = currentNodeId.intValue()+1;
			}
		}
	}
	/**
	 *   prioritySelectionFun
	 *  优先选择函数
	 * @param Number currentNodeId
	 * @see core.NetModel#prioritySelectionFun()
	 */
	public Number prioritySelectionFun(Number currentNodeId) {
		// TODO Auto-generated method stub
		double p = MathTool.random()*((double)this.nodeDegreeMax/this.degreeSum);//产生一个0~nodeDegreeMax/degreeSum的随机数
		Number oldNodeId = null;
		int flag = 0;
		do{
			oldNodeId = this.getRandNodeId();
			if(++flag > this.nodeNum){
				p = MathTool.random()*((double)this.nodeDegreeMax/this.degreeSum);//产生一个0~nodeDegreeMax/degreeSum的随机数
			}
		}while(p > (this.nodeDegree.get(oldNodeId).doubleValue()/this.degreeSum) || this.isHasEdge(currentNodeId, oldNodeId));
		int temp1 = 0, temp2 = 0;
		
		if(this.nodeDegree.get(currentNodeId) == null){
			//第一次添加该节点
			this.nodeDegree.put(currentNodeId, 1);
		}else{
			temp1 = (Integer) this.nodeDegree.get(currentNodeId);
			this.nodeDegree.remove(currentNodeId);
			this.nodeDegree.put(currentNodeId, temp1+1);
		}
		
		temp2 = (Integer) this.nodeDegree.get(oldNodeId);
		this.nodeDegree.remove(oldNodeId);
		this.nodeDegree.put(oldNodeId, temp2+1);
		
		if(this.nodeDegreeMax < temp2+1){
			this.nodeDegreeMax = temp2+1;
		}
		
		this.degreeSum +=2;
		return oldNodeId;
	}
}
